package com.app.backend.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.codec.binary.Base64;
import sun.misc.BASE64Decoder;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;

/**
 * aes tool
 *
 * @author zhun.xiao
 * @date 2018/1/27 9:55
 */
@Slf4j
public class AesEncryptUtil {

    /**
     * 算法
     */
    private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";

    private static final String CIPHER_ALGORITHM_CBC = "AES/CBC/NoPadding";

    private static final String ENCODING_UTF_8 = "UTF-8";

    private static final String AES = "AES";


    /**
     * 将byte[]转为各种进制的字符串
     *
     * @param bytes byte[]
     * @param radix 可以转换进制的范围，从Character.MIN_RADIX到Character.MAX_RADIX，超出范围后变为10进制
     * @return 转换后的字符串
     */
    public static String binary(byte[] bytes, int radix) {
        return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数
    }

    /**
     * base 64 encode
     *
     * @param bytes 待编码的byte[]
     * @return 编码后的base 64 code
     */
    public static String base64Encode(byte[] bytes) {
        return Base64.encodeBase64String(bytes);
    }

    /**
     * base 64 decode
     *
     * @param base64Code 待解码的base 64 code
     * @return 解码后的byte[]
     * @throws Exception
     */
    public static byte[] base64Decode(String base64Code) throws Exception {
        return StringUtils.isEmpty(base64Code) ? null : new BASE64Decoder().decodeBuffer(base64Code);
    }


    /**
     * 加密方法 返回byte数组
     *
     * @param data 要加密的数据
     * @param key  加密key
     * @param iv   加密ivs
     * @return 加密的结果
     * @throws Exception
     */
    public static byte[] encrypt(String data, String key, String iv) throws Exception {
        //"算法/模式/补码方式"NoPadding PKCS5Padding
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC);
        int blockSize = cipher.getBlockSize();

        byte[] dataBytes = data.getBytes();
        int plaintextLength = dataBytes.length;
        if (plaintextLength % blockSize != 0) {
            plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
        }
        byte[] plaintext = new byte[plaintextLength];
        System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);

        SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), AES);
        IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());

        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
        return cipher.doFinal(plaintext);
    }

    /**
     * 加密后生成Base 64字符串
     *
     * @param content    待加密的内容
     * @param encryptKey 加密密钥
     * @param iv         偏移量
     * @return 加密后的Base 64字符串
     */
    public static String base64Encrypt(String content, String encryptKey, String iv) throws Exception {
        return new Base64().encodeToString(encrypt(content, encryptKey, iv));
    }

    /**
     * 加密后生成16进制字符串
     *
     * @param content    待加密的内容
     * @param encryptKey 加密密钥
     * @param iv         偏移量
     * @return 加密后的16进制字符串
     */
    public static String HexEncrypt(String content, String encryptKey, String iv) throws Exception {
        return ByteUtil.bytesToHexString(encrypt(content, encryptKey, iv));
    }

    /**
     * AES加密为base 64 code 兼容旧版本
     *
     * @param content    待加密的内容
     * @param encryptKey 加密密钥
     * @return 加密后的base 64 code
     * @throws Exception
     */
    public static String aesEncrypt(String content, String encryptKey, String iv) throws Exception {
        return base64Encrypt(content, encryptKey, iv);
    }


    /**
     * 将base 64 code AES解密
     *
     * @param encryptStr 待解密的base 64 code
     * @param decryptKey 解密密钥
     * @return 解密后的string
     * @throws Exception
     */
    public static String aesDecrypt(String encryptStr, String decryptKey, String iv) throws Exception {
        return StringUtils.isEmpty(encryptStr) ? null : decryptByBase64(encryptStr, decryptKey, iv);
    }


    /**
     * 解密方法
     *
     * @param data 要解密的数据
     * @param key  解密key
     * @param iv   解密iv
     * @return 解密的结果
     * @throws Exception
     */
    public static String decrypt(byte[] data, String key, String iv) throws Exception {
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC);
        SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), AES);
        IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        byte[] original = cipher.doFinal(data);
        String originalString = new String(original, ENCODING_UTF_8);
        String res = originalString.trim();
        return res;
    }

    /**
     * AES解密，输入密文16进制字符串,默认使用UTF8编码
     *
     * @param cipherText  待解密
     * @param aesKey      加密Key
     * @param ivParameter 偏移量
     * @return String
     */
    public static String decryptByHex(String cipherText, String aesKey, String ivParameter) throws Exception {
        byte[] cipherTextByte = ByteUtil.hexStringToBytes(cipherText);
        if (cipherTextByte == null) {
            return null;
        }
        return decrypt(cipherTextByte, aesKey, ivParameter);
    }

    /**
     * AES解密，输入Base64字符串,默认使用UTF8编码
     *
     * @param cipherText  待解密
     * @param aesKey      加密Key
     * @param ivParameter 偏移量
     * @return String
     */
    public static String decryptByBase64(String cipherText, String aesKey, String ivParameter) throws Exception {
        byte[] cipherTextByte = new Base64().decode(cipherText);
        if (cipherTextByte == null) {
            return null;
        }
        return decrypt(cipherTextByte, aesKey, ivParameter);
    }


    public static void main(String[] args) throws Exception {
        String iv = "abc1234567890abc";
        String text = "test";
        String key = "abc1234567890abc";
        String s = AesEncryptUtil.aesEncrypt(text, key, iv);
        System.out.println(s);
        String s2 = AesEncryptUtil.HexEncrypt(text, key, iv);
        System.out.println(s2);
        System.out.println(AesEncryptUtil.decryptByHex("3b2ec0e556bd755e769ebbb7c5485939", key, iv));
        System.out.println(AesEncryptUtil.decryptByBase64("OMq3MXx1u/pLlcBhMCTQfg==", key, iv));
    }
}
